#pragma once
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <cstring>

static const int Default_socketfd = -1;
static const int Default_retries_interval = 1;
static const int Default_max_retries = 5;
static const int BUFFER_SIZE = 1024;

enum Error
{
    SOCKET_ERR = 1,
    CONNECT_ERR,

};

enum class Status // C++11 强类型枚举
{
    NEW,        // 新建连接状态
    CONNECTING, // 正在连接，可有可无，仅方便查看连接的状态
    CONNECTED,  // 连接成功
    DISCONNECT, // 重连失败
    CLOSED,     // 连接失败，由重连失败过渡过来，表示无法连接
    FINISH
};

// 具体状态接口
class ClientConnection // 客户端连接状态类
{
public:
    ClientConnection(const std::string &ip, const uint16_t &port)
        : _status(Status::NEW), _socketfd(Default_socketfd), _retries_interval(Default_retries_interval), _max_retries(Default_max_retries),
          _ip(ip), _port(port)
    {
    }
    ~ClientConnection() {}
    void Connect()
    {
        //  _status = Status::CONNECTING; // 可有可无
        // 1. 创建socket
        _socketfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_socketfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            exit(Error::SOCKET_ERR);
        }

        /// 2. 创建server的socket地址
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        inet_pton(AF_INET, _ip.c_str(), &server.sin_addr);
        //       server.sin_addr.s_addr=inet_addr(_ip.c_str());
        if (connect(_socketfd, (struct sockaddr *)(&server), sizeof(server)) < 0)
        {
            Disconnect();                 // 关闭原有socket，恢复默认值，重新连接分配socket
            _status = Status::DISCONNECT; // 重新设置状态
            return;                       //   出错了直接就不执行了
        }
        // 到这里说明连接成功了
        _status = Status::CONNECTED;
    }
    void Process()
    {
        //   _status=Status::CONNECTING;
        while (_status == Status::CONNECTED)
        {
            char buffer[BUFFER_SIZE];
            std::string message;
            std::cout << "client say# ";
            std::getline(std::cin, message);
            // if(message.size())  message+='\n';  // 确保打印到缓冲区的时候可以刷新，缓冲区为行刷新
            int n = ::write(_socketfd, message.c_str(), message.size());
            if (n > 0)
            {
                int m = ::read(_socketfd, buffer, sizeof(buffer) - 1);
                if (m > 0)
                {
                    buffer[m] = 0;
                    std::cout << buffer;
                }
                else
                    _status = Status::DISCONNECT; // 出错，只需要修改状态即可
            }
            else if (n == 0)
                _status = Status::FINISH; // 通信完成了，关闭通信
            else
                _status = Status::DISCONNECT;
        }
    }
    void Reconnect()
    {
        int count = 0;
        while (count < _max_retries)
        {
            count++;
            Connect();
            if (_status == Status::CONNECTED)
            {
                return;
            }
            std::cerr << "retries count: " << count << std::endl;
            sleep(_retries_interval);
        }
        _status = Status::CLOSED; // 重连失败
    }
    void Disconnect()
    {
        if (_socketfd != -1)
        {
            close(_socketfd);
            _socketfd = -1;
        }
    }

    void Setstatus(const Status &status)
    {
        _status = status;
    }
    const Status &GetStatus()
    {
        return _status;
    }

private:
    Status _status;        // 连接状态
    int _socketfd;         // 连接套接字
    uint16_t _port;        // 端口
    std::string _ip;       // IP地址
    int _retries_interval; // 重连时间间隔
    int _max_retries;      // 最大重连次数
};

// 上下文
class Client // 客户端类，基于客户端连接状态进行操作
{
public:
    Client(const std::string &ip, const uint16_t &port)
        : _conn(ip, port)
    {
    }
    void Excute()
    {
        while (true)
        {
            switch (_conn.GetStatus())
            {
            case Status::NEW:
                _conn.Connect();
                break;
            case Status::CONNECTED:
                std::cout << "connect success..." << std::endl;
                _conn.Process();
                break;
            // case Status::CONNECTING:
            //     _conn.Process();
            //     break;
            case Status::DISCONNECT:
                std::cerr << "connect error, try reconnect " << std::endl;
                _conn.Reconnect();
                break;
            case Status::CLOSED:
                std::cerr << "reconnect failure..." << std::endl;
                _conn.Disconnect();
                return;
            case Status::FINISH:
                _conn.Disconnect();
                return;
            default:
                break;
            }
        }
    }

private:
    ClientConnection _conn; // 可以继承也可以组装，但这种情况下，我们一般会选择组装
};